Skip to content

[RadeonFlow] flydsl mxfp4 a4w4 MoE: bring gemm1/gemm2 to parity with the HIP backend - #3828

Merged
coderfeli merged 3 commits into
ROCm:dev/randomflow_prfrom
RadeonFlow:radeonflow_pr_flydsl_perf
Jun 22, 2026
Merged

[RadeonFlow] flydsl mxfp4 a4w4 MoE: bring gemm1/gemm2 to parity with the HIP backend#3828
coderfeli merged 3 commits into
ROCm:dev/randomflow_prfrom
RadeonFlow:radeonflow_pr_flydsl_perf

Conversation

@ColorsWind

Copy link
Copy Markdown
Contributor

Summary

Brings the flydsl-ported mxfp4 (a4w4) MoE gemm1/gemm2 kernels on gfx950 up to the
HIP backend's performance: closes the large-M (prefill) gap to parity while keeping
the decode/mid-batch lead. Numerically identical (cos ≥ 0.999991 vs HIP, =1 at large M).

Changes

ABI (both kernels + their wrappers)

  • Raw-pointer kernel ABI: pass each tensor's data_ptr() (int64) instead of a
    full memref/fx.Tensor descriptor. Kernels assume contiguity and derive sizes from
    compile-time constants, so descriptor shape/stride fields are dead weight; bare
    addresses let the kernarg s_loads coalesce. ~7% faster at decode, parity by M≈64.

gemm2 (aiter/ops/flydsl/kernels/mxfp4_gemm2.py)

  • Backend-managed K-loop waitcnt (BM128 nonatomic): drop the hand-tuned
    inline-asm s_waitcnt vmcnt(N) and let the LLVM waitcnt pass own it. The inline
    asm was opaque to that pass, which double-inserted its own waitcnt — the large-M
    scheduling bubble.
  • Epilog output-address strength reduction: hoist the one runtime row*N_OUT
    i64 multiply out of the per-element store loop; per-element offsets become
    compile-time constants folded into the store address.
  • Unsigned index division for the non-negative grid/tile/count index math
    (drops signed-division sign-correction SALU).

gemm1 (aiter/ops/flydsl/kernels/mxfp4_gemm1.py)

  • Hardware exp2 in silu_mul (rocdl.exp2v_exp_f32) instead of the
    software math.exp2 expansion (matches HIP's __expf).

Measurement

Per-gemm device time via a cudagraph microbench: capture N back-to-back gemm
launches into one hipgraph, replay, and divide the event-timed elapsed by N
(isolates the gemm from the surrounding pipeline). Exclusive GPU, several
interleaved trials, reported as the in-run flydsl/hip ratio.

Results

Kimi-K2.5 TP=4 shape (NE=385, D_HIDDEN=7168, D_INTER=512, TOPK=9), gfx950,
per-gemm device time, flydsl/hip ratio (lower = faster than HIP):

M gemm2 before gemm2 after gemm1 before gemm1 after
4 0.92 0.94 0.93 0.93
256 0.93 0.93 1.07 1.01
2048 0.94 0.93 1.03 1.00
4096 1.11 1.00 1.07 1.05
16384 1.10 1.03 1.07 1.04
  • gemm2: faster/parity through M=2048; large-M deficit ~10% -> ~0–3%.
  • gemm1: faster/parity through M=2048; large-M deficit ~7% -> ~4%.

Notes

TODO

  • Continue closing the remaining large-M (prefill, M >= 4096) gap to HIP: gemm1
    ~4% (MFMA/K-loop bound, K = D_HIDDEN = 7168 dominates), gemm2 ~3% at M = 16384.

Pass bare data_ptr() device addresses (fx.Int64) for the global buffer args
instead of full memref descriptors. The kernels only need base pointers
(contiguity + compile-time sizes), so the addresses pack contiguously into
kernargs -> coalesced s_load prologue. ~7% faster at decode (M=4/8),
converging to parity by M=64. cos preserved.
- backend-managed K-loop waitcnt: drop the hand-tuned inline-asm vmcnt that
  the LLVM waitcnt pass double-inserted (the large-M scheduling bubble); let
  rocdl.barrier() own the load->ds_read wait.
- epilog output-address strength reduction: hoist the loop-invariant row*N_OUT
  i64 multiply out of the per-element store loop; per-element offsets become
  compile-time constants folded into the store address.
- unsigned index division (divui/remui) for the non-negative grid/tile/count
  index math, dropping signed-division sign-correction SALU.

M=4096 1.11 -> 1.00 (parity with HIP), M=16384 1.10 -> 1.03; faster/parity
through M=2048 unchanged. cos preserved.
Use rocdl.exp2 (v_exp_f32) in silu_mul instead of the software math.exp2
expansion (matches HIP __expf). Removes ~31% of non-MFMA VALU (the v_ldexp +
range-clamp v_cmp/v_cndmask the polynomial emitted); large-M ~2-6% faster
(M=4096 1.07 -> 1.05). cos preserved.
@coderfeli
coderfeli merged commit b242ba6 into ROCm:dev/randomflow_pr Jun 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants